import requests
import json
import re
import psycopg2
import logging
import traceback

import pymysql
from pymysql import MySQLError

from alibabacloud_polardb20170801.client import Client as polardb20170801Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_polardb20170801 import models as polardb_20170801_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient


from info_config import db_user
from info_config import db_password
from info_config import pg_info_url
from info_config import polardb_info_url
from info_config import net_type
from info_config import mongo_net_type
from info_config import mongodb_info_url


def url_get(url):
    headers = {"content-type": "application/json"}
    # requestData = {"name": "lance", "age": "28"}
    ret = requests.get(url, headers=headers)
    # if ret.status_code == 200:
    text = json.loads(ret.text)
    return text


def judge_ip(ipAddr):
    compile_ip = re.compile(
        "^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$"
    )
    if compile_ip.match(ipAddr):
        return True
    else:
        return False


def push_simple_metric(
    metric_name, value, instance_id, resource_name="-", gateway_url=""
):
    metric_data = f'{metric_name}{{instanceId="{instance_id}" ,resourceName="{resource_name}"}} {value}'
    # print(metric_data)
    response = requests.post(gateway_url, data=metric_data)
    if response.status_code in (200, 204):
        logging.info(f"Metric '{metric_name}' with value {value} pushed successfully.")
    else:
        logging.info(
            f"Failed to push metric '{metric_name}'. Response code: {response.status_code}"
        )


def prometheus_push(gateway_url, metric_data):
    response = requests.post(gateway_url, data=metric_data)
    if response.status_code in (200, 204):
        logging.info("metrics push successfully")
        return 1
    else:
        logging.info("metrics push failed")
        return 0


def create_conn(host, port, dbname, timeout=10):
    try:
        conn = psycopg2.connect(
            dbname=dbname,
            user=db_user,
            password=db_password,
            host=host,
            port=port,
            connect_timeout=timeout,
        )
        return conn
    except Exception as e:
        traceback.print_exc()


def create_mysql_conn(host, port, user, password, timeout=10):
    try:
        conn = pymysql.connect(
            host=host, port=port, user=user, password=password, connect_timeout=timeout
        )
        logging.info("Connection successful")
        return conn
    except pymysql.MySQLError as e:
        traceback.print_exc()
        return None


def get_pg_replicat_client_addr(pg_conn):
    sql = "select client_addr from pg_stat_replication;"
    r = pg_exec(pg_conn, sql)
    if len(r) != 0:
        return r[0][0]
    return


def get_all_databases(pg_conn):
    sql = """SELECT datname FROM pg_database WHERE datistemplate = false and datname not in ('polardb_admin','rdsadmin');"""
    r = pg_exec(pg_conn, sql)
    return r


def get_pg_invalid_index(pg_conn):
    sql = """SELECT
    n.nspname AS schema_name,
    c.relname AS index_name,
    i.indisvalid,
    i.indisready
FROM
    pg_index i
    JOIN pg_class c ON c.oid = i.indexrelid
    JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE
    NOT i.indisvalid OR NOT i.indisready;"""
    r = pg_exec(pg_conn, sql)
    return r


def get_pg_wal_size(pg_conn):
    sql = "select sum(size) /1024/1024/1024 as size_gb from pg_ls_waldir() ;"
    r = pg_exec(pg_conn, sql)
    if r is not None:
        return r[0][0]
    return r


def mysql_create_conn(host, port, timeout=10):
    """Creates and returns a MySQL connection using pymysql."""
    try:
        conn = pymysql.connect(
            host=host,
            port=port,
            user=db_user,
            password=db_password,
            connect_timeout=timeout,
        )
        logging.info("Connection to MySQL database was successful")
        return conn
    except MySQLError as e:
        logging(f"Error: {e}")
        return None


def get_mysql_server_id(conn):
    """Gets the MySQL server ID and returns it."""
    try:
        with conn.cursor() as cursor:
            cursor.execute("SHOW VARIABLES LIKE 'server_id';")
            result = cursor.fetchone()
            if result:
                return result[1]
            else:
                return None
    except MySQLError as e:
        logging.error(f"Error: {e}")
        return None


def pg_exec(pg_conn, sql):
    try:
        cur = pg_conn.cursor()
        cur.execute(sql)
        res = cur.fetchall()
        cur.close()
        return res
    except Exception as e:
        logging.error(pg_conn)
        traceback.print_exc()
        return None


def get_rds_mysql_resource_info():
    rds_info = url_get(pg_info_url)
    rds_data = rds_info.get("data")
    r_list = []
    for i in rds_data:
        id = i.get("id")
        name = i.get("name")
        status = i.get("status")
        # connect_info = get_connect_info(i, "postgresql")
        if status == "Running":
            engine = i["data"]["engine"]
            if engine != "MySQL":
                continue
            try:
                conn = i["data"]["connections"][net_type]
                conn_to_p = get_can_connect(conn)
                r_list.append([id, name, conn_to_p])
            except KeyError as e:
                pass
    return r_list


def get_rds_resource_info():
    # return [["pgm-0jlh04c0eko884g2","wl-data-collection-pg",{"host":"pgm-0jlh04c0eko884g27o.pg.rds.aliyuncs.com","port":5432}]]
    rds_info = url_get(pg_info_url)
    rds_data = rds_info.get("data")
    r_list = []
    for i in rds_data:
        id = i.get("id")
        name = i.get("name")
        status = i.get("status")
        region = i.get("region")
        provider = i.get("provider")
        # connect_info = get_connect_info(i, "postgresql")
        if (
            status == "Running"
            and region in ["cn-wulanchabu", "cn-shanghai"]
            and provider in ["cloudsim地图账号", "生产账号", "隔离账号"]
        ):
            engine = i["data"]["engine"]
            if engine != "PostgreSQL":
                continue
            try:
                conn = i["data"]["connections"][net_type]
                conn_to_p = get_can_connect(conn)
                r_list.append([id, name, conn_to_p])
            except KeyError as e:
                pass
    return r_list


def get_mysql_resource_info():
    rds_info = url_get(pg_info_url)
    rds_data = rds_info.get("data")
    r_list = []
    for i in rds_data:
        id = i.get("id")
        name = i.get("name")
        status = i.get("status")
        # connect_info = get_connect_info(i, "postgresql")
        if status == "Running":
            engine = i["data"]["engine"]
            if engine != "MySQL":
                continue
            try:
                conn = i["data"]["connections"][net_type]
                conn_to_p = get_can_connect(conn)
                r_list.append([id, name, conn_to_p])
            except KeyError as e:
                pass
    return r_list


def get_can_connect(con_string_list):
    for i in con_string_list:
        if judge_ip(i.get("host")):
            continue
        return i


def get_polardb_resource_info():
    polardb_info = url_get(polardb_info_url)
    polardb_data = polardb_info.get("data")
    r_list = []
    for i in polardb_data:
        id = i.get("id")
        name = i.get("name")
        status = i.get("status")
        # connect_info = get_connect_info(i, "postgresql")
        if status == "Running":
            address_items = i["data"]["address_items"]
            try:
                for i in address_items:
                    if i["net_type"] == net_type:
                        r_list.append(
                            [
                                id,
                                name,
                                {"host": i.get("connection"), "port": i.get("port")},
                            ]
                        )
            except Exception as e:
                traceback.print_exc()
    return r_list


def get_resource_info():
    rds_list = get_rds_resource_info()
    polardb_list = get_polardb_resource_info()
    return rds_list + polardb_list


def get_mongo_resource_info():
    rds_info = url_get(mongodb_info_url)
    rds_data = rds_info.get("data")
    r_list = []
    for info in rds_data:
        id = info.get("id")
        name = info.get("name")
        status = info.get("status")
        # connect_info = get_connect_info(i, "postgresql")
        if status == "Running":
            try:
                primary_connections = info["data"]["connections"].get(
                    f"{mongo_net_type}/Primary"
                )
                secondary_connections = info["data"]["connections"].get(
                    (f"{mongo_net_type}/Secondary")
                )
                if primary_connections is not None:
                    for i in primary_connections:
                        match_obj = re.match(r"(.*aliyuncs.com):(\d+).*", i.get("host"))
                        if match_obj is not None:
                            host = match_obj[1]
                            port = match_obj[2]
                            r_list.append(
                                [id, name, {"host": host, "port": port}, "primary"]
                            )
                if secondary_connections is not None:
                    for i in secondary_connections:
                        match_obj = re.match(r"(.*aliyuncs.com):(\d+).*", i.get("host"))
                        if match_obj is not None:
                            host = match_obj[1]
                            port = match_obj[2]
                            r_list.append(
                                [id, name, {"host": host, "port": port}, "secondary"]
                            )
            except KeyError as e:
                traceback.print_exc()
    return r_list


def create_aliyun_client(access_key_id, access_key_secret) -> polardb20170801Client:
    """
    使用AK&SK初始化账号Client
    @return: Client
    @throws Exception
    """
    # 工程代码泄露可能会导致 AccessKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考。
    # 建议使用更安全的 STS 方式，更多鉴权访问方式请参见：https://help.aliyun.com/document_detail/378659.html。
    config = open_api_models.Config(
        # 必填，请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。,
        access_key_id=access_key_id,
        # 必填，请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。,
        access_key_secret=access_key_secret,
    )
    # Endpoint 请参考 https://api.aliyun.com/product/polardb
    config.endpoint = f"polardb.aliyuncs.com"
    return polardb20170801Client(config)


def get_polardb_status(aliyun_client, dbcluster_ids) -> None:
    describe_dbclusters_request = polardb_20170801_models.DescribeDBClustersRequest(
        region_id="cn-wulanchabu", dbcluster_ids=dbcluster_ids
    )
    runtime = util_models.RuntimeOptions()
    try:
        # 复制代码运行请自行打印 API 的返回值
        r = aliyun_client.describe_dbclusters_with_options(
            describe_dbclusters_request, runtime
        )
        return r.body
    except Exception as error:
        # 此处仅做打印展示，请谨慎对待异常处理，在工程项目中切勿直接忽略异常。
        # 错误 message
        logging.error(error.message)
        # 诊断地址
        logging.error(error.data.get("Recommend"))
        UtilClient.assert_as_string(error.message)
